﻿using HZY.Repository.Admin.Entities.Systems;

namespace HZY.Api.Admin.ApplicationServices.Systems;

public class SysFileService : ApplicationService<SysFile, Guid, SysFile, SysFile>
{
    private readonly IAccountService _accountService;
    private readonly IFileManager _fileManager;
    private readonly HZY.Core.UploadFile.FileOptions _fileOptions;

    public SysFileService(IServiceProvider serviceProvider,
        IAccountService accountService,
        IFileManager fileManager,
        HZY.Core.UploadFile.FileOptions fileOptions) : base(serviceProvider)
    {
        _accountService = accountService;
        _fileManager = fileManager;
        _fileOptions = fileOptions;
    }

    public override async Task<PagingView> FindListAsync(PagingSearchInput<SysFile> pagingSearchInput)
    {
        var query = Repository.SelectNoTracking
                .OrderByDescending(w => w.CreationTime)
                .Select(w => new
                {
                    w.Id,
                    w.FileName,
                    w.OriginalName,
                    w.FileSize,
                    w.FileType,
                    w.Url,
                    w.Sha,
                    w.MimeType,
                    w.CreationTime,
                    w.LastModificationTime,
                    WebUrl = _fileOptions.ServerUrl + w.Url
                });

        var result = await Repository.AsPagingViewAsync(query, pagingSearchInput);
        // //覆盖值
        // result
        //     .FormatValue(query, w => w.CreationTime, (oldValue) => oldValue.ToString("yyyy-MM-dd"))
        //     .FormatValue(query, w => w.LastModificationTime, (oldValue) => oldValue?.ToString("yyyy-MM-dd"))
        //     ;
        return result;
    }

    public override async Task DeleteListAsync(List<Guid> ids)
    {
        var list = await Repository.SelectNoTracking.Where(w => ids.Contains(w.Id)).ToListAsync();

        // 从数据库查询不是这些id，但sha相同的文件
        var duplicateFiles = await Repository.SelectNoTracking
            .Where(w => !ids.Contains(w.Id))
            .Where(w => list.Select(s => s.Sha).Contains(w.Sha))
            .Select(w => new
            {
                w.Id,
                w.FileName,
                w.Sha
            })
            .ToListAsync();

        // 删除文件
        var (deleteFiles, ex) = _fileManager
            .BuildFileManagerContext()
            .FileDelete(
                list.Where(w => duplicateFiles.Select(s => s.FileName).Contains(w.FileName) && duplicateFiles.Select(s => s.Sha).Contains(w.Sha))
                    .Select(w => w.FileName)
            );

        // 如果删除没有报错，删除数据库记录
        if (ex == null)
        {
            await Repository.SelectNoTracking.Where(w => ids.Contains(w.Id)).ExecuteDeleteAsync();
            return;
        }
        
        // 如果删除文件失败，只把删除成功的文件从数据库删除
        var fileIds = list.Where(w => string.IsNullOrWhiteSpace(w.FileName) || deleteFiles.Contains(w.FileName)).Select(s => s.Id).ToList();
        
        await Repository.SelectNoTracking.Where(w => fileIds.Contains(w.Id)).ExecuteDeleteAsync();
    }

    /// <summary>
    /// 上传文件
    /// </summary>
    /// <param name="file"></param>
    /// <returns></returns>
    /// <exception cref="MessageBox"></exception>
    public async Task<SysFileUploadResultDto> UploadFileAsync(IFormFile file)
    {
        var sysFileUploadResult = new SysFileUploadResultDto();
        var fileContextInfo = await _fileManager
            .BuildFileManagerContext()
            .FileUploadAsync(file)
            .SuccessAsync(async info =>
            {
                var sysFile= await Repository.InsertAsync(new SysFile()
                {
                    Url = info.FileWebUrl,
                    FileName = info.FileName,
                    OriginalName = info.OldName,
                    FileSize = info.FileSize,
                    FileType = info.FileType,
                    Sha = info.Sha,
                    MimeType = Tools.GetMimeType(info.OldName)
                });
                sysFileUploadResult = sysFile.MapTo<SysFile, SysFileUploadResultDto>();
                sysFileUploadResult.WebUrl = info.FileRoot;
            })
            .ExistsAsync(async info =>
            {
                var fileEntity = await Repository.SelectNoTracking
                    .Where(w => w.FileName == info.FileName)
                    .FirstOrDefaultAsync();

                if (fileEntity == null || fileEntity.CreatorUserId != _accountService.AccountContext.Id)
                {
                    var sysFile = await Repository.InsertAsync(new SysFile()
                    {
                        Url = info.FileWebUrl,
                        FileName = info.FileName,
                        OriginalName = info.OldName,
                        FileSize = info.FileSize,
                        FileType = info.FileType,
                        Sha = info.Sha,
                        MimeType = Tools.GetMimeType(info.OldName)
                    });

                    sysFileUploadResult = sysFile.MapTo<SysFile, SysFileUploadResultDto>();
                    sysFileUploadResult.WebUrl = info.FileRoot;
                    return;
                }

                sysFileUploadResult = fileEntity.MapTo<SysFile, SysFileUploadResultDto>();
                sysFileUploadResult.WebUrl = info.FileRoot;
            })
            .Error(info => throw MessageBox.CreateMessage(R.ErrorMessage(info.Exception?.Message ?? "上传失败")));

        return sysFileUploadResult;
    }
    
    /// <summary>
    /// 下载文件
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    /// <exception cref="MessageBox"></exception>
    public async Task<IActionResult> DownloadFileAsync(Guid id)
    {
        var file = await Repository.SelectNoTracking.FirstOrDefaultAsync(w => w.Id == id);
        
        if (file == null || string.IsNullOrWhiteSpace(file.FileName))
        {
            throw MessageBox.Show("文件不存在");
        }

        var filePath = _fileManager.BuildFileManagerContext().GetFileRoot(file.FileName);

        await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        using var memoryStream = new MemoryStream();
        await fileStream.CopyToAsync(memoryStream);

        return new FileContentResult(memoryStream.ToArray(), Tools.GetMimeType(file.MimeType))
        {
            FileDownloadName = file.OriginalName
        };
    }
}